package tablaSimbolos;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

public class TablaSimbolos {
	private Hashtable<String,EntradaClase> TC;
	EntradaClase claseActual;
	EntradaMetodo metodoActual;
	
	
	public TablaSimbolos(){
		TC = new Hashtable<String, EntradaClase>(11,7);
		init();		
	}	
	public EntradaClase agregarClase(String lex){
		EntradaClase ent = new EntradaClase(TC.get("Object"),lex);
		TC.put(lex,ent);
		return ent;
	}
	public EntradaMetodo agregarMetodo(String lex,String tipo){
		return claseActual.agregarMetodo(lex,tipo);
	}
	public EntradaMetodo agregarMetodo(EntradaMetodo e){
		return claseActual.agregarMetodo(e);
	}
	public EntradaConstructor agregarConstructor(){
		EntradaConstructor cons = claseActual.setConstructor();
		cons. declarar(true);
		return cons;
	}
	public void agregarParam(String lex,String tipo){		
		metodoActual.agregarParam(lex,tipo);
	}	
	public EntradaClase getClase(String s){
		return TC.get(s);
	}
	public EntradaClase getClaseActual(){
		return claseActual;
	}
	public EntradaClase setClaseActual(String c){
		claseActual = TC.get(c);
		return claseActual;
	}
	public EntradaClase setClaseActual(EntradaClase c){
		claseActual = c;
		return claseActual;
	}
	public EntradaMetodo getMetodoActual(){
		return metodoActual;
	}
	public EntradaMetodo setMetodoActual(String m){
		if (m.equals(claseActual.getNombre()))
			metodoActual = claseActual.getConstructor();
		else
			metodoActual = claseActual.getMetodo(m);
		return metodoActual;
	}
	public EntradaMetodo setMetodoActual(EntradaMetodo m){
		metodoActual = m;
		return metodoActual;
	}	
	public boolean chequearParamDef(String nombre, String tipo){
		return metodoActual.chequearParamDef(nombre,tipo);
	}
	public String chequearParamInvoc(String c, String met,String tipo,String esNull){
		EntradaVariable param = getClase(c).getMetodo(met).returnParamInvoc();
		if (param == null)
				return "ausencia de parametros";
		else{
			if(esConformante(param.getTipo(),tipo,esNull))
				return null;
			return param.getTipo();
		}
	}
	public boolean chequearParaReDef(EntradaMetodo met,String lexParam,String tipoParam){
		return met.chequearParamDef(lexParam, tipoParam); 
	}
	
	
	public String chequearDefiniciones(){
		boolean estanDefinidas = true;
		String claseSinDefinir = null;
		Iterator<EntradaClase> it = TC.values().iterator();
		EntradaClase clase = null;
		while(it.hasNext() && estanDefinidas){
			clase = it.next();
			estanDefinidas = estanDefinidas && clase.estaDefinida();
		}
		if(!estanDefinidas && clase != null)
			claseSinDefinir = clase.getNombre();
		return claseSinDefinir;
	}
	public String chequearMetodos(){
		return claseActual.chequearMetodos();
	}
	
	public EntradaMetodo getMetodoHeredado(String lex){
		return getMetodoHeredadoAux(lex,claseActual.getPadre()); 
	}

	/*
	 * chequea que no haya herencia circular con la clase actual
	 * */
	public boolean esHerenciaCircular(EntradaClase c){
		EntradaClase clasePadre = c.getPadre();
		if (clasePadre == null)
			return false; //estoy en Object
		if(clasePadre.getNombre().equals(claseActual.getNombre()))
			return true;
		else
			return esHerenciaCircular(clasePadre);
	}
	public boolean esConformante(String Tesperado, String Trecibido,String esNull){
		if(Tesperado.equals("int") || Tesperado.equals("boolean") || Tesperado.equals("char") || Tesperado.equals("String") || Tesperado.equals("void")){
			return Tesperado.equals(Trecibido);
		}
		if(Trecibido.equals("int") || Trecibido.equals("boolean") || Trecibido.equals("char") || Trecibido.equals("String") || Trecibido.equals("void")){
			return Tesperado.equals(Trecibido); 
		}
		if(esNull.equals("true"))
			return true;//ya se q es de clase. Si me llego null, entonces esta todo bien
		else
			return esConfTClase(Tesperado,Trecibido);
	}

	private boolean esConfTClase(String Tesp, String Trec){ //aca ya se que es de tipo clase
		EntradaClase c = getClase(Trec).getPadre();
		if (Tesp.equals(Trec)) //si coinciden los nombres, los tipos son conformantes
			return true;
		if (c == null)
			return false;
		//si el padre es nulo y llegue ac�, recorri todo el arbol de herencia sin encontrar el subtipo //correcto (llegu� hasta Object).
		else
			return esConfTClase(Tesp, c.getNombre());
		}
	private EntradaMetodo getMetodoHeredadoAux(String lex,EntradaClase c){
		if (c == null){
			return null; //llegu� la la clase objeto
		}
		EntradaMetodo resul = c.getMetodo(lex);
		if (resul != null)
			return resul;
		else
			return getMetodoHeredadoAux(lex, c.getPadre());
	}	
	public void offsetear(){
		Iterator<EntradaClase> it = TC.values().iterator();
		while (it.hasNext()){
			EntradaClase c = it.next();
			c.offsetear();
		}
	}
	private void init(){
		
		agregarClase("Object");
		setClaseActual("Object").definida(true);
		setClaseActual("Object").declarada(true);
	//	agregarConstructor();
		
		agregarClase("System");		
		setClaseActual("System").definida(true);
		setClaseActual("System").declarada(true);
		agregarConstructor();
		agregarMetodo("read","int").setOffset(0);
		
		
		agregarMetodo("printB","void").setOffset(1);
		setMetodoActual("printB");
		agregarParam("b","boolean");
		
		agregarMetodo("printBln","void").setOffset(6);
		setMetodoActual("printBln");
		agregarParam("b","boolean");
		
		agregarMetodo("printC","void").setOffset(2);
		setMetodoActual("printC");
		agregarParam("c","char");
		
		agregarMetodo("printCln","void").setOffset(7);
		setMetodoActual("printCln");
		agregarParam("c","char");		
		
		agregarMetodo("printI","void").setOffset(3);
		setMetodoActual("printI");
		agregarParam("i","int");
		
		agregarMetodo("printIln","void").setOffset(8);
		setMetodoActual("printIln");
		agregarParam("i","int");
		
		agregarMetodo("printS","void").setOffset(4);
		setMetodoActual("printS");
		agregarParam("s","String");
		
		agregarMetodo("printSln","void").setOffset(9);
		setMetodoActual("printSln");
		agregarParam("s","String");
		
		agregarMetodo("println","void").setOffset(5);

	}
	
	public static void main(String[] args) {
		TablaSimbolos TS = new TablaSimbolos();
		System.out.println(TS.getClase("System").getMetodo("printS").getParamValor("s"));
	}

}
